package com.earthea.chatbot.login.service.impl;

import com.earthea.cache.CacheService;
import com.earthea.chatbot.common.entity.BaseRsp;
import com.earthea.chatbot.common.exception.ServiceException;
import com.earthea.chatbot.domain.LoginUser;
import com.earthea.chatbot.domain.User;
import com.earthea.chatbot.login.common.config.security.context.AuthenticationContextHolder;
import com.earthea.chatbot.login.common.constant.Constants;
import com.earthea.chatbot.login.common.exception.CaptchaException;
import com.earthea.chatbot.login.common.exception.UserPasswordNotMatchException;
import com.earthea.chatbot.login.entity.request.LoginReq;
import com.earthea.chatbot.login.entity.request.RegisterReq;
import com.earthea.chatbot.login.entity.response.LoginRsp;
import com.earthea.chatbot.login.service.TokenService;
import com.earthea.chatbot.login.service.UserService;
import com.earthea.chatbot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 用户服务类
 */
@Service
public class UserServiceImpl implements UserService {
    @Value("${app.captcha.enabled:true}")
    private boolean captchaEnabled;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private BCryptPasswordEncoder encoder;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private CacheService cacheService;

    @Override
    public BaseRsp register(RegisterReq registerReq) {
        String msg = "";
        String username = registerReq.getUsername();
        String email = registerReq.getEmail();
        String password = registerReq.getPassword();
        User user = new User();
        user.setUsername(username);
        user.setEmail(email);

        // 验证码开关
        if (captchaEnabled) {
            validateCaptcha(registerReq.getCode(), registerReq.getUuid());
        }

        if (checkUserNameUnique(username) != null) {
            msg = "保存用户'" + username + "'失败，注册用户名已存在";
        } else if (checkEmailUnique(email) != null) {
            msg = "保存用户'" + email + "'失败，注册邮箱已存在";
        } else {
            user.setPassword(encoder.encode(password));
            if (userMapper.insert(user) <= 0) {
                msg = "注册失败,请联系系统管理人员";
            }
        }
        return BaseRsp.success(msg);
    }

    /**
     * 校验验证码
     *
     * @param code 验证码
     * @param uuid 唯一标识
     */
    private void validateCaptcha(String code, String uuid) {
        String verifyKey = Constants.CAPTCHA_CODES_CACHE_NAME + uuid;
        String captcha = cacheService.get(verifyKey);
        cacheService.del(verifyKey);
        if (captcha == null) {
            throw new CaptchaException("验证码已失效");
        }
        if (!code.equalsIgnoreCase(captcha)) {
            throw new CaptchaException("验证码错误");
        }
    }

    @Override
    public LoginRsp login(LoginReq loginReq) {
        LoginRsp result = BaseRsp.success(LoginRsp.class);
        // 验证码开关
        if (captchaEnabled) {
            validateCaptcha(loginReq.getCode(), loginReq.getUuid());
        }
        // 用户验证
        Authentication authentication = null;
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginReq.getNameOrEmail(), loginReq.getPassword());
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException();
            } else {
                throw new ServiceException(e);
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 生成token
        String token = tokenService.createToken(loginUser, loginReq.getRememberMe());
        result.setToken(token);
        return result;
    }

    @Override
    public User selectByUser(String nameOrEmail) {
        return userMapper.selectByUserName(nameOrEmail);
    }

    @Override
    public User checkUserNameUnique(String username) {
        return userMapper.checkUserNameUnique(username);
    }

    @Override
    public User checkEmailUnique(String email) {
        return userMapper.checkEmailUnique(email);
    }

    @Override
    public int update(User user) {
        return userMapper.update(user);
    }
}
